查看原文
其他

Python 玩出花儿了!一文教你用 Python 制作吃豆人游戏! | 附代码

李秋键 AI科技大本营 2020-12-18

作者 | 李秋键
责编 | Carol
封图 | CSDN 下载自视觉中国

近几年来Python语言得到了快速发展,而Pygame作为Python开发应用和游戏必备的库更是展现了Python的优越性。

而今天我们就将借助Pygame建立吃豆人游戏。

吃豆人是电子游戏历史上的经典街机游戏,由Namco公司的岩谷彻设计并由Midway Games在1980年发行。Pac-Man被认为是80年代最经典的街机游戏之一,游戏的主角小精灵的形象甚至被作为一种大众文化符号,或是此产业的代表形象。

而Pygame模块是跨平台Python模块,专为电子游戏设计,包含图像、声音。建立在SDL基础上,允许实时电子游戏研发而无需被低级语言(如机器语言和汇编语言)束缚。

最终游戏效果如下可见:

 


一、实验前的准备


首先我们使用的python版本是3.6.5所用到的模块是pygame模块,用来创建游戏框架。Random模块用来随机生成方向。


素材准备


首先我们将图片放到images目录下,背景音乐放到sounds目录下。

如下图可见:

游戏搭建


1、定义一些精灵:

整体的类变量定义包括墙类,通过pygame的图片填充作为墙类的加载;同理还包括食物类和角色。而怪物的随机运动使用random产生随机运动方向。

其对应的代码如下:

import random
import pygame
'''墙类'''
class Wall(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, color, **kwargs):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.left = x
        self.rect.top = y
'''食物类'''
class Food(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, color, bg_color, **kwargs):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height])
        self.image.fill(bg_color)
        self.image.set_colorkey(bg_color)
        pygame.draw.ellipse(self.image, color, [00, width, height])
        self.rect = self.image.get_rect()
        self.rect.left = x
        self.rect.top = y
'''角色类'''
class Player(pygame.sprite.Sprite):
    def __init__(self, x, y, role_image_path):
        pygame.sprite.Sprite.__init__(self)
        self.role_name = role_image_path.split('/')[-1].split('.')[0]
        self.base_image = pygame.image.load(role_image_path).convert()
        self.image = self.base_image.copy()
        self.rect = self.image.get_rect()
        self.rect.left = x
        self.rect.top = y
        self.prev_x = x
        self.prev_y = y
        self.base_speed = [3030]
        self.speed = [00]
        self.is_move = False
        self.tracks = []
        self.tracks_loc = [00]
    '''改变速度方向'''
    def changeSpeed(self, direction):
        if direction[0] < 0:
            self.image = pygame.transform.flip(self.base_image, True, False)
        elif direction[0] > 0:
            self.image = self.base_image.copy()
        elif direction[1] < 0:
            self.image = pygame.transform.rotate(self.base_image, 90)
        elif direction[1] > 0:
            self.image = pygame.transform.rotate(self.base_image, -90)
        self.speed = [direction[0] * self.base_speed[0], direction[1] * self.base_speed[1]]
        return self.speed
    '''更新角色位置'''
    def update(self, wall_sprites, gate_sprites):
        if not self.is_move:
            return False
        x_prev = self.rect.left
        y_prev = self.rect.top
        self.rect.left += self.speed[0]
        self.rect.top += self.speed[1]
        is_collide = pygame.sprite.spritecollide(self, wall_sprites, False)
        if gate_sprites is not None:
            if not is_collide:
                is_collide = pygame.sprite.spritecollide(self, gate_sprites, False)
        if is_collide:
            self.rect.left = x_prev
            self.rect.top = y_prev
            return False
        return True
    '''生成随机的方向'''
    def randomDirection(self):
        return random.choice([[-0.50], [0.50], [00.5], [0, -0.5]])

2、游戏关卡定义:

在这里设置好了关卡一。关卡的定义必须包括墙的位置,不同关卡墙的位置和怪物的位置不同。更多关卡可以参照设置

对应代码如下:

import pygame
from Sprites import *
NUMLEVELS = 1
'''关卡一'''
class Level1():
    def __init__(self):
        self.info = 'level1'
    '''创建墙'''
    def setupWalls(self, wall_color):
        self.wall_sprites = pygame.sprite.Group()
        wall_positions = [[006600],
                          [006006],
                          [06006066],
                          [60006606],
                          [3000666],
                          [60601866],
                          [360601866],
                          [60120666],
                          [601206126],
                          [1801202466],
                          [300120666],
                          [480120666],
                          [5401206126],
                          [1201801266],
                          [1201806126],
                          [3601801266],
                          [4801806126],
                          [1802406126],
                          [1803602466],
                          [4202406126],
                          [240240426],
                          [324240426],
                          [240240666],
                          [2403001266],
                          [360240666],
                          [0300666],
                          [540300666],
                          [60360666],
                          [603606186],
                          [480360666],
                          [5403606186],
                          [1204203666],
                          [120420666],
                          [480420666],
                          [1804802466],
                          [300480666],
                          [1205401266],
                          [3605401266]]
        for wall_position in wall_positions:
            wall = Wall(*wall_position, wall_color)
            self.wall_sprites.add(wall)
        return self.wall_sprites
    '''创建门'''
    def setupGate(self, gate_color):
        self.gate_sprites = pygame.sprite.Group()
        self.gate_sprites.add(Wall(282242422, gate_color))
        return self.gate_sprites
    '''创建角色'''
    def setupPlayers(self, hero_image_path, ghost_images_path):
        self.hero_sprites = pygame.sprite.Group()
        self.ghost_sprites = pygame.sprite.Group()
        self.hero_sprites.add(Player(287439, hero_image_path))
        for each in ghost_images_path:
            role_name = each.split('/')[-1].split('.')[0]
            if role_name == 'Blinky':
                player = Player(287199, each)
                player.is_move = True
                player.tracks = [[0-0.54], [0.509], [00.511], [0.503], [00.57], [-0.5011], [00.53],
                                 [0.5015], [0-0.515], [0.503], [0-0.511], [-0.503], [0-0.511], [-0.503],
                                 [0-0.53], [-0.507], [0-0.53], [0.5015], [00.515], [-0.503], [00.53],
                                 [-0.503], [0-0.57], [-0.503], [00.57], [-0.5011], [0-0.57], [0.505]]
                self.ghost_sprites.add(player)
            elif role_name == 'Clyde':
                player = Player(319259, each)
                player.is_move = True
                player.tracks = [[-102], [0-0.54], [0.505], [00.57], [-0.5011], [0-0.57],
                                 [-0.503], [00.57], [-0.507], [00.515], [0.5015], [0-0.53],
                                 [-0.5011], [0-0.57], [0.503], [0-0.511], [0.509]]
                self.ghost_sprites.add(player)
            elif role_name == 'Inky':
                player = Player(255259, each)
                player.is_move = True
                player.tracks = [[102], [0-0.54], [0.5010], [00.57], [0.503], [0-0.53],
                                 [0.503], [0-0.515], [-0.5015], [00.53], [0.5015], [00.511],
                                 [-0.503], [0-0.57], [-0.5011], [00.53], [-0.5011], [00.57],
                                 [-0.503], [0-0.53], [-0.503], [0-0.515], [0.5015], [00.53],
                                 [-0.5015], [00.511], [0.503], [0-0.511], [0.5011], [00.53], [0.501]]
                self.ghost_sprites.add(player)
            elif role_name == 'Pinky':
                player = Player(287259, each)
                player.is_move = True
                player.tracks = [[0-14], [0.509], [00.511], [-0.5023], [00.57], [0.503],
                                 [0-0.53], [0.5019], [00.53], [0.503], [00.53], [0.503],
                                 [0-0.515], [-0.507], [00.53], [-0.5019], [0-0.511], [0.509]]
                self.ghost_sprites.add(player)
        return self.hero_sprites, self.ghost_sprites
    '''创建食物'''
    def setupFood(self, food_color, bg_color):
        self.food_sprites = pygame.sprite.Group()
        for row in range(19):
            for col in range(19):
                if (row == 7 or row == 8and (col == 8 or col == 9 or col == 10):
                    continue
                else:
                    food = Food(30*col+3230*row+3244, food_color, bg_color)
                    is_collide = pygame.sprite.spritecollide(food, self.wall_sprites, False)
                    if is_collide:
                        continue
                    is_collide = pygame.sprite.spritecollide(food, self.hero_sprites, False)
                    if is_collide:
                        continue
                    self.food_sprites.add(food)
        return self.food_sprites

3、游戏创建:

在通过关卡定义墙等位置后以及精灵自身属性怪物运动、食物定义等后,通过调用已经创建好的类达到搭建游戏的目的。

具体如下可见:

import os
import sys
import pygame
import Levels
'''定义一些必要的参数'''
BLACK = (000)
WHITE = (255255255)
BLUE = (00255)
GREEN = (02550)
RED = (25500)
YELLOW = (2552550)
PURPLE = (2550255)
SKYBLUE = (0191255)
BGMPATH = os.path.join(os.getcwd(), 'resources/sounds/bg.mp3')
ICONPATH = os.path.join(os.getcwd(), 'resources/images/icon.png')
FONTPATH = os.path.join(os.getcwd(), 'resources/font/ALGER.TTF')
HEROPATH = os.path.join(os.getcwd(), 'resources/images/pacman.png')
BlinkyPATH = os.path.join(os.getcwd(), 'resources/images/Blinky.png')
ClydePATH = os.path.join(os.getcwd(), 'resources/images/Clyde.png')
InkyPATH = os.path.join(os.getcwd(), 'resources/images/Inky.png')
PinkyPATH = os.path.join(os.getcwd(), 'resources/images/Pinky.png')
'''开始某一关游戏'''
def startLevelGame(level, screen, font):
    clock = pygame.time.Clock()
    SCORE = 0
    wall_sprites = level.setupWalls(SKYBLUE)
    gate_sprites = level.setupGate(WHITE)
    hero_sprites, ghost_sprites = level.setupPlayers(HEROPATH, [BlinkyPATH, ClydePATH, InkyPATH, PinkyPATH])
    food_sprites = level.setupFood(YELLOW, WHITE)
    is_clearance = False
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit(-1)
                pygame.quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    for hero in hero_sprites:
                        hero.changeSpeed([-10])
                        hero.is_move = True
                elif event.key == pygame.K_RIGHT:
                    for hero in hero_sprites:
                        hero.changeSpeed([10])
                        hero.is_move = True
                elif event.key == pygame.K_UP:
                    for hero in hero_sprites:
                        hero.changeSpeed([0-1])
                        hero.is_move = True
                elif event.key == pygame.K_DOWN:
                    for hero in hero_sprites:
                        hero.changeSpeed([01])
                        hero.is_move = True
            if event.type == pygame.KEYUP:
                if (event.key == pygame.K_LEFT) or (event.key == pygame.K_RIGHT) or (event.key == pygame.K_UP) or (event.key == pygame.K_DOWN):
                    hero.is_move = False
        screen.fill(BLACK)
        for hero in hero_sprites:
            hero.update(wall_sprites, gate_sprites)
        hero_sprites.draw(screen)
        for hero in hero_sprites:
            food_eaten = pygame.sprite.spritecollide(hero, food_sprites, True)
        SCORE += len(food_eaten)
        wall_sprites.draw(screen)
        gate_sprites.draw(screen)
        food_sprites.draw(screen)
        for ghost in ghost_sprites:
            # 幽灵随机运动(效果不好且有BUG)
            '''
            res = ghost.update(wall_sprites, None)
            while not res:
                ghost.changeSpeed(ghost.randomDirection())
                res = ghost.update(wall_sprites, None)
            '''

            # 指定幽灵运动路径
            if ghost.tracks_loc[1] < ghost.tracks[ghost.tracks_loc[0]][2]:
                ghost.changeSpeed(ghost.tracks[ghost.tracks_loc[0]][02])
                ghost.tracks_loc[1] += 1
            else:
                if ghost.tracks_loc[0] < len(ghost.tracks) - 1:
                    ghost.tracks_loc[0] += 1
                elif ghost.role_name == 'Clyde':
                    ghost.tracks_loc[0] = 2
                else:
                    ghost.tracks_loc[0] = 0
                ghost.changeSpeed(ghost.tracks[ghost.tracks_loc[0]][02])
                ghost.tracks_loc[1] = 0
            if ghost.tracks_loc[1] < ghost.tracks[ghost.tracks_loc[0]][2]:
                ghost.changeSpeed(ghost.tracks[ghost.tracks_loc[0]][02])
            else:
                if ghost.tracks_loc[0] < len(ghost.tracks) - 1:
                    loc0 = ghost.tracks_loc[0] + 1
                elif ghost.role_name == 'Clyde':
                    loc0 = 2
                else:
                    loc0 = 0
                ghost.changeSpeed(ghost.tracks[loc0][02])
            ghost.update(wall_sprites, None)
        ghost_sprites.draw(screen)
        score_text = font.render("Score: %s" % SCORE, True, RED)
        screen.blit(score_text, [1010])
        if len(food_sprites) == 0:
            is_clearance = True
            break
        if pygame.sprite.groupcollide(hero_sprites, ghost_sprites, FalseFalse):
            is_clearance = False
            break
        pygame.display.flip()
        clock.tick(10)
    return is_clearance
'''显示文字'''
def showText(screen, font, is_clearance, flag=False):
    clock = pygame.time.Clock()
    msg = 'Game Over!' if not is_clearance else 'Congratulations, you won!'
    positions = [[235233], [65303], [170333]] if not is_clearance else [[145233], [65303], [170333]]
    surface = pygame.Surface((400200))
    surface.set_alpha(10)
    surface.fill((128128128))
    screen.blit(surface, (100200))
    texts = [font.render(msg, True, WHITE),
             font.render('Press ENTER to continue or play again.'True, WHITE),
             font.render('Press ESCAPE to quit.'True, WHITE)]
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
                pygame.quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    if is_clearance:
                        if not flag:
                            return
                        else:
                            main(initialize())
                    else:
                        main(initialize())
                elif event.key == pygame.K_ESCAPE:
                    sys.exit()
                    pygame.quit()
        for idx, (text, position) in enumerate(zip(texts, positions)):
            screen.blit(text, position)
        pygame.display.flip()
        clock.tick(10)
'''初始化'''
def initialize():
    pygame.init()
    icon_image = pygame.image.load(ICONPATH)
    pygame.display.set_icon(icon_image)
    screen = pygame.display.set_mode([606606])
    pygame.display.set_caption('吃豆人')
    return screen
'''主函数'''
def main(screen):
    pygame.mixer.init()
    pygame.mixer.music.load(BGMPATH)
    pygame.mixer.music.play(-10.0)
    pygame.font.init()
    font_small = pygame.font.Font(FONTPATH, 18)
    font_big = pygame.font.Font(FONTPATH, 24)
    for num_level in range(1, Levels.NUMLEVELS+1):
        if num_level == 1:
            level = Levels.Level1()
            is_clearance = startLevelGame(level, screen, font_small)
            if num_level == Levels.NUMLEVELS:
                showText(screen, font_big, is_clearance, True)
            else:
                showText(screen, font_big, is_clearance)

最终运行程序结果如下:

 

源码地址:

https://pan.baidu.com/s/128id8L-PDPgGOPuH-5uHDg

提取码:rj9f

作者简介:李秋键,CSDN博客专家,CSDN达人课作者。硕士在读于中国矿业大学,开发有taptap竞赛获奖等等。

推荐阅读
你点的每个“在看”,我都认真当成了AI

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存